/* termcap - print termcap settings	Author: Terrence Holm */

#include <termcap.h>
#include <stdio.h>
#include <stdlib.h>

#define  TC_BUFFER  1024	/* Size of termcap(3) buffer    */

/****************************************************************/
/*								*/
/*    termcap  [ type ]						*/
/*								*/
/*    Prints out all of the termcap capabilities as described	*/
/*    in termcap(4). If "type" is not supplied then $TERM is	*/
/*    used.							*/
/*								*/
/****************************************************************/

void Print(char *comment, char *name);
void Error(char *message, char *arg);

int main(int argc, char *argv[])
{
    char *term;
    static char buffer[TC_BUFFER];

    /*  Check for an argument  */

    if (argc > 2)
	Error("Usage: %s [type]\n", argv[0]);

    if (argc == 2)
	term = argv[1];
    else
	term = getenv("TERM");

    if (term == NULL)
	Error("termcap: $TERM is not defined\n", "");


    /*  Read in the termcap entry  */

    if (tgetent(buffer, term) != 1)
	Error("termcap: No termcap entry for %s\n", term);


    /*  Print out the entry's contents  */

    printf("TERM = %s\n\n", term);

    if (tgetflag("am") == 1)
	printf("End of line wraps to next line   (am)\n");

    if (tgetflag("bs") == 1)
	printf("Ctrl/H performs a backspace      (bs)\n");

    printf("Number of columns                (co) = %d\n", tgetnum("co"));
    printf("Number of lines                  (li) = %d\n", tgetnum("li"));

    Print("Clear to end of line", "ce");
    Print("Clear to end of screen", "cd");
    Print("Clear the whole screen", "cl");

    Print("Start \"stand out\" mode", "so");
    Print("End \"stand out\" mode", "se");
    Print("Start underscore mode", "us");
    Print("End underscore mode", "ue");
    Print("Start blinking mode", "mb");
    Print("Start bold mode", "md");
    Print("Start reverse mode", "mr");
    Print("Return to normal mode", "me");

    Print("Scroll backwards", "sr");
    Print("Cursor motion", "cm");

    Print("Up one line", "up");
    Print("Down one line", "do");
    Print("Left one space", "le");
    Print("Right one space", "nd");
    Print("Move to top left corner", "ho");

    Print("Generated by \"UP\"", "ku");
    Print("Generated by \"DOWN\"", "kd");
    Print("Generated by \"LEFT\"", "kl");
    Print("Generated by \"RIGHT\"", "kr");
    Print("Generated by \"HOME\"", "kh");
    Print("Generated by \"END\"", "k0");
    Print("Generated by \"PGUP\"", "k1");
    Print("Generated by \"PGDN\"", "k2");
    Print("Generated by numeric \"+\"", "k3");
    Print("Generated by numeric \"-\"", "k4");
    Print("Generated by numeric \"5\"", "k5");

    return 0;
}


/****************************************************************/
/*								*/
/*    	Print(comment, name)					*/
/*								*/
/*    		If a termcap entry exists for "name", then	*/
/*		print out "comment" and the entry. Control	*/
/*		characters are printed as ^x.			*/
/*								*/
/****************************************************************/

void Print(char *comment, char *name)
{
    char entry[50];
    char *p = entry;

    if (tgetstr(name, &p) == NULL)
	return;

    printf("%-32s (%s) = ", comment, name);

    for (p = entry; *p != '\0'; ++p)
	if (*p < ' ')
	    printf("^%c", *p + '@');
	else if (*p == '\177')
	    printf("^?");
	else
	    putchar(*p);

    putchar('\n');
}


/****************************************************************/
/*								*/
/*    	Error(message, arg)					*/
/*								*/
/*    		Printf the "message" and abort.			*/
/*								*/
/****************************************************************/

void Error(char *message, char *arg)
{
    fprintf(stderr, message, arg);
    exit(1);
}

/****************************/

/*#include <termcap_lib.c>*/
/*
 *	termcap.c	V1.1	20/7/87		agc	Joypace Ltd
 *
 *	Copyright Joypace Ltd, London, UK, 1987. All rights reserved.
 *	This file may be freely distributed provided that this notice
 *	remains attached.
 *
 *	A public domain implementation of the termcap(3) routines.
 *
 *
 *
 *	 Klamer Schutte	      V1.2    Nov. 1988
 *
 *   - Can match multiple terminal names		 [tgetent]
 *   - Removal of **area assignments			 [tgetstr]
 *
 *	 Terrence W. Holm     V1.3    May, Sep, Oct.  1988
 *
 *   - Correct when TERM != name and TERMCAP is defined	 [tgetent]
 *   - Correct the comparison for the terminal name 	 [tgetent]
 *   - Correct the value of ^x escapes              	 [tgetstr]
 *   - Added %r to reverse row/column			 [tgoto]
 *   - Fixed end of definition test			 [tgetnum/flag/str]
 *
 *	 Terrence W. Holm     V1.4    Jan. 1989
 *
 *   - Incorporated Klamer's V1.2 fixes into V1.3
 *   - Added %d, (old %d is now %2)			 [tgoto]
 *   - Allow '#' comments in definition file		 [tgetent]
 */

#include <termcap.h>
#include <ctype.h>
/*#include <stdlib.h>
#include <stdio.h>*/
#include <string.h>

char *capab = (char *) NULL;	/* the capability itself */

#if 0
/*  The following are not yet used.  */
extern short ospeed;		/* output speed */
extern char PC;			/* padding character */
extern char *BC;		/* back cursor movement */
extern char *UP;		/* up cursor movement */
#endif


/*
 *	tgetent - get the termcap entry for terminal name, and put it
 *	in bp (which must be an array of 1024 chars). Returns 1 if
 *	termcap entry found, 0 if not found, and -1 if file not found.
 */

int tgetent(char *bp, const char *name)
{
    FILE *fp;
    char *file;
    char *term;
    short len = strlen(name);

    capab = bp;

    /* If TERMCAP begins with a '/' then use TERMCAP as the path   */
    /* Name of the termcap definitions file. If TERMCAP is a       */
    /* Definition and TERM equals "name" then use TERMCAP as the   */
    /* Definition. Otherwise use "/etc/termcap" as the path name.  */

    if ((file = getenv("TERMCAP")) == (char *) NULL)
	file = "/etc/termcap";
    else if (*file != '/')
	if ((term = getenv("TERM")) != (char *) NULL
	    && strcmp(term, name) == 0) {
	    *bp = '\0';
	    strncat(bp, file, 1023);
	    return (1);
	} else
	    file = "/etc/termcap";

    if ((fp = fopen(file, "r")) == (FILE *) NULL) {
	capab = (char *) NULL;	/* no valid termcap  */
	return (-1);
    }
    for (;;) {
	/* Read in each definition */
	int def_len = 0;
	char *cp = bp;

	do {
	    if (fgets(&bp[def_len], (unsigned int) (1024 - def_len), fp) ==
		(char *) NULL) {
		fclose(fp);
		capab = (char *) NULL;	/* no valid termcap */
		return (0);
	    }
	    def_len = strlen(bp) - 2;
	} while (bp[def_len] == '\\');

	while (isspace(*cp))
	    cp++;

	/* Comment lines start with a '#'  */
	if (*cp == '#')
	    continue;

	/* See if any of the terminal names in this definition */
	/* Match "name".                                       */

	do {
	    if (strncmp(name, cp, len) == 0 &&
		(cp[len] == '|' || cp[len] == ':')) {
		fclose(fp);
		return (1);
	    }
	    while ((*cp) && (*cp != '|') && (*cp != ':'))
		cp++;
	} while (*cp++ == '|');
    }
}


/*
 *	tgetnum - get the numeric terminal capability corresponding
 *	to id. Returns the value, -1 if invalid.
 */

int tgetnum(char *id)
{
    register char *cp = capab;

    if (cp == (char *) NULL || id == (char *) NULL)
	return (-1);

    for (;;) {
	while (*cp++ != ':')
	    if (cp[-1] == '\0')
		return (-1);

	while (isspace(*cp))
	    cp++;

	if (strncmp(cp, id, 2) == 0 && cp[2] == '#')
	    return (atoi(cp + 3));
    }
}


/*
 *	tgetflag - get the boolean flag corresponding to id. Returns -1
 *	if invalid, 0 if the flag is not in termcap entry, or 1 if it is
 *	present.
 */

int tgetflag(char *id)
{
    register char *cp = capab;

    if (cp == (char *) NULL || id == (char *) NULL)
	return (-1);

    for (;;) {
	while (*cp++ != ':')
	    if (cp[-1] == '\0')
		return (0);

	while (isspace(*cp))
	    cp++;

	if (strncmp(cp, id, 2) == 0)
	    return (1);
    }
}


/*
 *	tgetstr - get the string capability corresponding to id and place
 *	it in area (advancing area at same time). Expand escape sequences
 *	etc. Returns the string, or NULL if it can't do it.
 */

char *tgetstr(char *id, char **area)
{
    register char *cp = capab;
    register char *wsp = *area;	/* workspace pointer  */

    if (cp == (char *) NULL || id == (char *) NULL)
	return ((char *) NULL);

    for (;;) {
	while (*cp++ != ':')
	    if (cp[-1] == '\0')
		return ((char *) NULL);

	while (isspace(*cp))
	    cp++;

	if (strncmp(cp, id, 2) == 0 && cp[2] == '=') {
	    for (cp += 3; *cp && *cp != ':'; wsp++, cp++)
		switch (*cp) {
		case '^':
		    *wsp = *++cp - '@';
		    break;

		case '\\':
		    switch (*++cp) {
		    case 'E':
			*wsp = '\033';
			break;
		    case 'n':
			*wsp = '\n';
			break;
		    case 'r':
			*wsp = '\r';
			break;
		    case 't':
			*wsp = '\t';
			break;
		    case 'b':
			*wsp = '\b';
			break;
		    case 'f':
			*wsp = '\f';
			break;
		    case '0':
		    case '1':
		    case '2':
		    case '3':
			{
			    int i;
			    int t = 0;
			    for (i = 0; i < 3 && isdigit(*cp); ++i, ++cp)
				t = t * 8 + *cp - '0';
			    *wsp = t;
			    cp--;
			    break;
			}
		    default:
			*wsp = *cp;
		    }
		    break;

		default:
		    *wsp = *cp;
		}

	    *wsp++ = '\0';

	    {
		char *ret = *area;
		*area = wsp;
		return (ret);
	    }
	}
    }				/* end for(;;) */
}


/*
 *	tgoto - given the cursor motion string cm, make up the string
 *	for the cursor to go to (destcol, destline), and return the string.
 *	Returns "OOPS" if something's gone wrong, or the string otherwise.
 */

char *tgoto(const char *cm, int destcol, int destline)
{
    static char ret[24];
    char *rp = ret;
    int incr = 0;
    int argno = 0;
    int numval;

    for (; *cm; cm++) {
	if (*cm == '%') {
	    switch (*++cm) {
	    case 'i':
		incr = 1;
		break;

	    case 'r':
		argno = 1;
		break;

	    case '+':
		numval = (argno == 0 ? destline : destcol);
		*rp++ = numval + incr + *++cm;
		argno = 1 - argno;
		break;

	    case '2':
		numval = (argno == 0 ? destline : destcol);
		numval = (numval + incr) % 100;
		*rp++ = '0' + (numval / 10);
		*rp++ = '0' + (numval % 10);
		argno = 1 - argno;
		break;

	    case 'd':
		numval = (argno == 0 ? destline : destcol);
		numval = (numval + incr) % 1000;
		if (numval > 99)
		    *rp++ = '0' + (numval / 100);
		if (numval > 9)
		    *rp++ = '0' + (numval / 10) % 10;
		*rp++ = '0' + (numval % 10);
		argno = 1 - argno;
		break;

	    case '%':
		*rp++ = '%';
		break;

	    default:
		return ("OOPS");
	    }

	} else
	    *rp++ = *cm;
    }

    *rp = '\0';
    return (ret);
}


/*
 *	tputs - put the string cp out onto the terminal, using the function
 *	outc. This should do padding for the terminal, but I can't find a
 *	terminal that needs padding at the moment...
 */

int tputs(const char *cp, int affcnt, 
        int (*outc)(int ch))
{
    if (cp == (const char *) NULL)
	return (1);
    /* Do any padding interpretation - left null for MINIX just now */
    while (*cp)
	(*outc) (*cp++);
    return (1);
}
